Categories
Node.js Best Practices

Node.js Best Practices — Environment and Clusters

Spread the love

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices we should follow when writing Node apps.

DevOps Tools

DevOps tools will make configuring our environment and server setup easier.

Also, we need a process manager to restart our Express app automatically in case it crashes.

And we need a reverse proxy and load balancer to expose our app to the Internet, cache requests, and balance the load across multiple worker processes.

This lets us maintain high performance in our app.

Managing Environment Variables in Node.js with dotenv

The dotenv library is very useful for letting us read variables from an .env file

For example, we can write:

NODE_ENV=production
DEBUG=false

in our .env file.

And then we can read it with:

require('dotenv').config()
​
const express = require('express')
const app = express()

It’ll read the environment variables from a file named .env by default.

However, it can also read from multiple environment variable files.

Make Sure the Application Restarts Automatically with a Process Manager

If we have an Express app, it’ll crash when an unhandled error is encountered.

Therefore, it’s important that we restart our app automatically so that it won’t be down.

We can do that with Systemd by creating a new file in /lib/systemd/system called app.service :

[Unit]
Description=Node.js as a system service.
Documentation=https://example.com
After=network.target
[Service]
Type=simple
User=ubuntu
ExecStart=/usr/bin/node /my-app/server.js
Restart=on-failure
[Install]
WantedBy=multi-user.target

The Service section has the ExecStart line which runs our app on startup.

It also has the Restart line to restart on failure.

We can then reload the daemon start the script with:

systemctl daemon-reload
systemctl start fooapp
systemctl enable fooapp
systemctl status fooapp

PM2

PM2 is a process manager that lets us manage the Express app process.

We can install it by running:

npm i -g pm2

Then we can run our app by running:

pm2 start server.js -i max

-i max is the max number of threads to run our app with.

This way, it’ll spawn enough workers to use all CPU cores.

Load Balancing and Reverse Proxies

The Node cluster module lets us spawn worker processes that serve our app.

We can create a cluster by writing:

const cluster = require('cluster')
const numCPUs = require('os').cpus().length
const app = require('./src/app')
const port = process.env.PORT || 8888
​
const masterProcess = () => Array.from(Array(numCPUs)).map(cluster.fork)
const childProcess = () => app.listen(port)
​
if (cluster.isMaster) {
  masterProcess()
} else {
  childProcess()
}
​
cluster.on('exit', () => cluster.fork())

The master process creates the cluster and the child presses listen for requests at the given port.

The masterProcess counts how many CPU cores there are and calls cluser.fork to create child processes equal to the number of cores available.

The exit event listener will restart the process if it fails with cluster.fork .

Then in our Systemd file, we replace:

ExecStart=/usr/bin/node /my-app/server.js

with:

ExecStart=/usr/bin/node /my-app/cluster.js

And then we can restart Systemd with:

systemctl daemon-reload
systemctl restart fooapp

Conclusion

We can read environment variables with dotenv and add clusters with the cluster module.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *